#include "Functions.h"

int main(int argc, char **argv) {
    //mywords: vetor de palavras que cada no vai receber.
    //amount_bytes: quantidade de bytes que cada no vai receber
	//msg: mensagem de sinalizacao indicando final ou continuação do processo de geracao de palavras
	//n_words: quantidade de palavras de cada no
	//n_found: quantidade de palavras encontradas em cada no ate o presente momento
    int rc, myrank, n_procs, amount_bytes, msg;
    char **mywords;
    unsigned long int n_words, n_found;
    unsigned register int i;
    struct timeval *begin;
	
    if(argc != 2) {
        printf("Usage: %s <percentage>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    rc = MPI_Init(&argc, &argv);
    if(rc != MPI_SUCCESS) {
        printf("Error starting MPI program. Aborting...\n");
        MPI_Abort(MPI_COMM_WORLD, rc);
    }
    MPI_Comm_size(MPI_COMM_WORLD, &n_procs);
    MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
    if(myrank == 0) {   //o no mestre executa esse bloco de codigo
        unsigned long int total;
		unsigned long int pos_byte = 0; //posicao do byte no arquivo
        struct timeval *end;

        begin = malloc(sizeof(struct timeval));
        end = malloc(sizeof(struct timeval));
        gettimeofday(begin, NULL);
        if(ReadFile("palavras.txt"))
            return EXIT_FAILURE;
        if(AllocateData(n_procs))
            return EXIT_FAILURE;
        SetParameters(n_procs, atof(argv[1]));
		n_words = file->AVG_WORDS_PER_PROC;
        mywords = malloc((n_words+1)*sizeof(char*));
        for(i = 0; i < (n_words+1); i++) {
            mywords[i] = malloc(MAX_SIZE_WORD*sizeof(char));
        }
        amount_bytes = GetAmountBytes();
        n_words = GetWords(0, amount_bytes, mywords);
		pos_byte += amount_bytes;
        for(i = 1; i < n_procs; i++) {      //para cada no, o no mestre envia um byte inicial e um byte final a ser mapeado
            MPI_Ssend(&file->AVG_WORDS_PER_PROC, 1, MPI_UNSIGNED_LONG, i, 0, MPI_COMM_WORLD); //envia a qnt de palavras para o no
            MPI_Ssend(&pos_byte, 1, MPI_UNSIGNED_LONG, i, 0, MPI_COMM_WORLD); //envia o byte inicial
            amount_bytes = GetAmountBytes();    //obtem a quantidade de bytes para um no
			pos_byte = pos_byte + amount_bytes - 1;
            MPI_Ssend(&pos_byte, 1, MPI_UNSIGNED_LONG, i, 0, MPI_COMM_WORLD); //envia o byte final
			pos_byte++;
        }
        n_found = 0;
        msg = 0;
        do {
			for(i = 0; i < n_procs; i++)
				MPI_Send(&msg, 1, MPI_INT, i, 0, MPI_COMM_WORLD); 	//envia a mensagem de sinalizacao para os nos escravos
            n_found += RandomWord(MAX_TRIES, n_words, mywords); 	//obtem a quantidade de palavras encontradas localmente
            MPI_Reduce(&n_found, &total, 1, MPI_UNSIGNED_LONG, MPI_SUM, 0, MPI_COMM_WORLD); 	//obtem a quantidade de palavras global (de todos os nos)
        } while(total < file->aim); 	//enquanto nao atingir a porcentagem definida de palavras
        gettimeofday(end, NULL);
        msg = 1;
		for(i = 0; i < n_procs; i++)
			MPI_Send(&msg, 1, MPI_INT, i, 0, MPI_COMM_WORLD); 	//envia a msg para os nos escravos encerrarem os seus trabalhos
        printf("Total de palavras encontradas: %ld\n", total);
        ShowElapsedTime(begin, end);
    } else if(myrank > 0) {    //os nos escravos executarao esse bloco de codigo
        MPI_Status status;
        int count, i, msg;
        char **mywords;
		unsigned long int initial_byte, final_byte;

        if(ReadFile("palavras.txt"))
            return EXIT_FAILURE;

        MPI_Recv(&n_words, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); 		//numero estimado de palavras
        MPI_Recv(&initial_byte, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); 	//byte inicial para leitura
        MPI_Recv(&final_byte, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); 		//byte final para leitura
        mywords = malloc((n_words+1)*sizeof(char*)); 		//aloca a matriz que guardara as palavras do conjunto de bytes
        for(i = 0; i < (n_words+1); i++) {
            mywords[i] = malloc(MAX_SIZE_WORD*sizeof(char));
        }
		n_words = GetWords(initial_byte, final_byte, mywords); 	//obtem as palavras do conjunto de bytes
        n_found = 0;
        do {
            MPI_Recv(&msg, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); 	//recebe a mensagem de sinalizacao do no mestre
            if(!msg) {
                n_found += RandomWord(MAX_TRIES, n_words, mywords); 	//obtem a quantidade de palavras encontradas
                MPI_Reduce(&n_found, NULL, 1, MPI_UNSIGNED_LONG, MPI_SUM, 0, MPI_COMM_WORLD); 	//envia a quantidade de palavras encontradas para o no mestre
            }
        } while(msg == 0);

    }
    MPI_Finalize();

    return EXIT_SUCCESS;
}
